﻿namespace EasyToolkit.ModelDefinition
{
    using System;
    using System.Linq;
    using System.Reflection;
    using EasyToolkit.Tools.Extensions;
    using EasyToolkit.ModelDefinition.Definition.XmlDefinition;
    using EasyToolkit.Tools.Reflection;

    class ModelAccessor : IModelAccessor
    {
        #region properties and attributes
        private string returnTypeName;
        private bool dependenciesResolved;

        public string Name
        { get; private set; }

        public string Description
        { get; private set; }

        private IModelType returnType;
        public IModelType ReturnType
        {
            get
            { 
                if(! dependenciesResolved)
                    ResolveDependencies();
                return returnType;
            }
        }

        public MethodInfo MethodInfo
        { get; private set; }

        public MethodInfo SingletonMethodInfo
        { get; private set; }

        public ParameterInfo[]  ParametersInfo
        { get; private set; }
        #endregion

        #region .ctors
        public ModelAccessor(XmlModelAccessorDefinition definition)
        {
            if (definition != null)
            {
                try
                {
                    definition.Name = definition.Name.EmptyIfNull().Trim();
                    definition.Type = definition.Type.EmptyIfNull().Trim();
                    definition.Assembly = definition.Assembly.EmptyIfNull().Trim();
                    definition.ReturnType = definition.ReturnType.EmptyIfNull().Trim();

                    if (string.IsNullOrEmpty(definition.Name.Trim()))
                        throw new EasyToolkitException("'Name' is mandatory");
                    if (string.IsNullOrEmpty(definition.Type))
                        throw new EasyToolkitException("'Type' is mandatory");
                    if (string.IsNullOrEmpty(definition.Assembly))
                        throw new EasyToolkitException("'Assembly' is mandatory");
                    if (string.IsNullOrEmpty(definition.ReturnType))
                        throw new EasyToolkitException("'ReturnType' is mandatory");

                    Name = definition.Name.Trim();
                    returnTypeName = definition.ReturnType.Trim();
                    Description = definition.Description;

                    Type type = TypeHelpers.GetType(definition.Assembly, definition.Type);
                    if (type == null)
                        throw new EasyToolkitException(string.Format("Type '{0}.{1}' not found.", definition.Assembly, definition.Type));

                    MethodInfo = type.GetMethod(definition.Name);
                    if (MethodInfo != null)
                        ParametersInfo = MethodInfo.GetParameters();
                    else
                    {
                        PropertyInfo property = type.GetProperty(definition.Name);
                        if (property != null)
                        {
                            MethodInfo = property.GetGetMethod();
                            ParametersInfo = new ParameterInfo[0];
                        }
                    }

                    if (MethodInfo == null)
                        throw new EasyToolkitException(string.Format("'{0}' not found.", definition.Name));

                    if (! MethodInfo.IsStatic)
                    {
                        if (string.IsNullOrEmpty(definition.SingletonAccessorName))
                            throw new EasyToolkitException(string.Format("'{0}' must be 'static'.", definition.Name));
                        else
                        {
                            string singletondName = definition.SingletonAccessorName.EmptyIfNull().Trim();
                            SingletonMethodInfo = type.GetMethod(singletondName);
                            if (SingletonMethodInfo != null)
                            {
                                ParameterInfo[] singletonParametersInfo = SingletonMethodInfo.GetParameters();
                                if (singletonParametersInfo.Count() > 0)
                                    throw new EasyToolkitException(string.Format("The singleton method accessor '{0}' cannot have parameters.", singletondName));
                            }
                            else
                            {
                                PropertyInfo property = type.GetProperty(definition.Name);
                                if (property != null)
                                    SingletonMethodInfo = property.GetGetMethod();
                            }

                            if (SingletonMethodInfo == null)
                                throw new EasyToolkitException(string.Format("The singleton accessor '{0}' cannot be found.", singletondName));
                            if (!SingletonMethodInfo.IsStatic)
                                throw new EasyToolkitException(string.Format("The singleton accessor '{0}' must be static.", singletondName));
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new EasyToolkitException(string.Format("Cannot create 'ModelAccessor' '{0} {1}.{2}': {3}", definition.ReturnType.EmptyIfNull(), definition.Type.EmptyIfNull(), definition.Name.EmptyIfNull(), ex.Message));
                }
            }
        }
        #endregion

        #region public method
        public void ResolveDependencies()
        { 
            if (! dependenciesResolved)
            {
                if (!string.IsNullOrEmpty(returnTypeName))
                {
                    returnType = ModelDefinitionManager.Instance.GetModelType(returnTypeName);
                    if (returnType == null)
                        throw new EasyToolkitException(string.Format("Accessor '{0}': return type '{1}' not found", Name.EmptyIfNull(), returnTypeName));
                }
                dependenciesResolved = true;
            }         
        }

        public object Invoke()
        {
            return null;
        }
        #endregion
    }
}
